package com.cl.ks.service;


import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.cl.ks.entity.*;
import com.cl.ks.flow.KsFlowGraphHandler;
import com.cl.ks.repository.KsDataFieldRepository;
import com.cl.ks.repository.SparkNodeRepository;
import com.cl.ks.utils.CommonResult;
import com.cl.mvc.service.BaseService;
import com.cl.mvc.util.UpdateUtil;
import com.cl.spark.dto.SparkParam;
import com.cl.spark.factory.SparkNodeFactory;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.cl.ks.constant.QTableConstant.*;

@Service
@Log
public class SparkNodeService extends BaseService<SparkNode> {
    private final KsDataFieldRepository ksDataFieldRepository;
    private final KsFlowGraphService ksFlowGraphService;

    private final JPAQueryFactory queryFactory;
    private final KsFlowGraphHandler ksFlowGraphHandler;
    private final KsRuleService ksRuleService;

    @Autowired
    public SparkNodeFactory sparkNodeFactory;
    @Autowired
    private ClueAutoResultService clueAutoResultService;

    public SparkNodeService(SparkNodeRepository sparkNodeRepository, KsDataFieldRepository ksDataFieldRepository, KsFlowGraphService ksFlowGraphService, JPAQueryFactory queryFactory, KsFlowGraphHandler ksFlowGraphHandler, KsRuleService ksRuleService) {
        super(sparkNodeRepository);
        this.ksDataFieldRepository = ksDataFieldRepository;
        this.ksFlowGraphService = ksFlowGraphService;
        this.queryFactory = queryFactory;
        this.ksFlowGraphHandler = ksFlowGraphHandler;
        this.ksRuleService = ksRuleService;
    }
    public CommonResult<JSONArray> getFields( SparkParam sparkParam) {
        if ("子流程".equals(sparkParam.getNodeName())) {

        }
        return CommonResult.success(sparkNodeFactory.getSparkNodeByName(sparkParam.getNodeName()).getFields(sparkParam));
    }

    public SparkNode saveCustom(SparkNode sparkNode) {

        String nodeValue = sparkNode.getNodeTemplate();
        LinkedHashMap nodeValueObj = JSONObject.parseObject(nodeValue, LinkedHashMap.class);

        Integer parentId = sparkNode.getParentId();
        Optional<SparkNode> parentNode = repository.findById(parentId);
        if (!parentNode.isPresent()) {
            throw new RuntimeException("parentId错误");
        }
        String nodeTemplate = parentNode.get().getNodeTemplate();
        LinkedHashMap nodeTemplateObj = JSONObject.parseObject(nodeTemplate, LinkedHashMap.class);

        nodeTemplateObj.forEach((nodeTemplateItemKey, value) -> {
            if (!nodeValueObj.containsKey(nodeTemplateItemKey)) {
                return;
            }
            String nodeValueItemValue = String.valueOf(nodeValueObj.get(nodeTemplateItemKey));
            LinkedHashMap nodeTemplateItem = JSONObject.parseObject(JSON.toJSONString(nodeTemplateObj.get(nodeTemplateItemKey)), LinkedHashMap.class);
            nodeTemplateItem.put("value", nodeValueItemValue);
            nodeTemplateObj.put(nodeTemplateItemKey, nodeTemplateItem);

        });


        LocalDateTime now = LocalDateTime.now();
        if (sparkNode.getCreateTime() == null) {
            sparkNode.setCreateTime(now);
        }
        if (sparkNode.getUpdateTime() == null) {
            sparkNode.setUpdateTime(now);
        }
        if (sparkNode.getId() != null) {
            SparkNode target = findById(sparkNode.getId()).get();
            target.setNodeTemplate(JSON.toJSONString(nodeTemplateObj, SerializerFeature.DisableCircularReferenceDetect));
            UpdateUtil.copyNullProperties(sparkNode, target);
            return repository.save(target);
        }
        sparkNode.setNodeTemplate(JSON.toJSONString(nodeTemplateObj, SerializerFeature.DisableCircularReferenceDetect));
        return repository.save(sparkNode);
    }

    public CommonResult downloadData(Integer ksFlowGraphId, HttpServletResponse response) {

        KsFlowGraph ksFlowGraph = queryFactory.selectFrom(qKsFlowGraph).where(qKsFlowGraph.id.eq(ksFlowGraphId)).fetchOne();
        KsFlowGraphLog ksFlowGraphLog = queryFactory.selectFrom(qKsFlowGraphLog).where(qKsFlowGraphLog.ksFlowGraphId.eq(ksFlowGraphId)).orderBy(qKsFlowGraphLog.createTime.desc()).limit(1L).fetchFirst();
        if (ksFlowGraphLog == null) {
            return CommonResult.failed("暂无数据");
        }
        List<KsFlowResult> ksFlowResultList = queryFactory.selectFrom(qKsFlowResult).where(qKsFlowResult.ksFlowGraphId.eq(ksFlowGraphId)).fetch();

        if (ksFlowResultList.isEmpty()) {
            return CommonResult.success("暂无数据");
        }

        Map<String, List<KsFlowResult>> map = new HashMap<>();
        ksFlowResultList.forEach(ksFlowResult -> {
            map.putIfAbsent(ksFlowResult.getNodeName(), new ArrayList<>());
            map.get(ksFlowResult.getNodeName()).add(ksFlowResult);
        });

        ExcelWriter writer = null;

        for (Map.Entry<String, List<KsFlowResult>> entry : map.entrySet()) {
//            String key = ChineseUtil.extractChinese(entry.getKey());
            String key = entry.getKey();
            List<KsFlowResult> value = entry.getValue();
            if (writer == null) {
                writer = new ExcelWriter(true, key);
            } else {
                writer.setSheet(key);
            }
            writer.clearHeaderAlias();

            if (!value.isEmpty()) {
                Set<String> itemKeySet = JSON.parseObject(value.get(0).getResult()).keySet();
                for (String itemKey : itemKeySet) {
                    writer.addHeaderAlias(itemKey, itemKey);
                }
            }

            writer.addHeaderAlias("zjhm", "证件号码");

            List<KsDataField> ksDataFieldList = ksDataFieldRepository.listKsDataField(JSON.parseObject(value.get(value.size() - 1).getResult()).keySet().toArray(new String[0]));
            for (KsDataField ksDataField : ksDataFieldList) {
                writer.addHeaderAlias(ksDataField.getCode(), ksDataField.getFieldName());
            }

            List<JSONObject> writeList = value.stream().map(KsFlowResult::getResult).map(item -> {
                try {
                    JSONObject jsonObject = JSON.parseObject(item);
                    if (item.length() < 32767) {
                        return jsonObject;
                    } else {
                        jsonObject.entrySet().forEach(itemEntry -> {
                            String itemValue = String.valueOf(itemEntry.getValue());
                            if (itemValue.length() > 32767) {
                                String trimValue = itemValue.replaceAll("\\s+", "");
                                if (trimValue.length() > 32767) {
                                    itemEntry.setValue("数据超长已截断：" + trimValue.substring(0, 32700));
                                } else {
                                    itemEntry.setValue(trimValue);
                                }
                            }
                        });
                    }
                    return jsonObject;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }).filter(Objects::nonNull).collect(Collectors.toList());


            writer.write(writeList);
        }

        String ksFlowGraphName = Objects.requireNonNull(ksFlowGraph).getName();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-hhmmss");
        String now = sdf.format(new Date());
        String resultFileName = ksFlowGraphName + "-" + now;
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + resultFileName + ".xls");
        ServletOutputStream out = null;
        try {
            out = response.getOutputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        if (writer == null) {
            writer = new ExcelWriter(true);
        }
        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);

        return null;
    }

    public CommonResult downloadData(List<Map<String, Object>> data, HttpServletResponse response) {

        ExcelWriter writer = ExcelUtil.getWriter(true);
        writer.write(data, true);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=result.xls");
        ServletOutputStream out = null;
        try {
            out = response.getOutputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);

        return CommonResult.success("下载成功");
    }

    public CommonResult handleAllKsFlowGraph(String auth) {
        List<KsFlowGraph> ksFlowGraphList = ksFlowGraphService.findAll();
        ksFlowGraphList.forEach(ksFlowGraph -> {
            handleKsFlowGraphById(ksFlowGraph.getId(), false, auth);
        });
        return CommonResult.success("", "success");
    }

    public CommonResult handleKsFlowGraphById(Integer ksFlowGraphId, Boolean testFlag, String auth) {
        try {

            KsFlowGraph ksFlowGraph = ksFlowGraphService.findById(ksFlowGraphId).get();
//        String showName = jwtUtil.getShowName(auth);
//        Integer userId = jwtUtil.getUserId(auth);
//        ksFlowGraph.setCreatedByShowName(showName);
//        ksFlowGraph.setCreatedBy(String.valueOf(userId));
            ksFlowGraph.setTestFlag(testFlag);
            ksFlowGraph.setAuth(auth);
            handleKsFlowGraph(ksFlowGraph);
            return CommonResult.success("", "任务提交成功");

        } catch ( Exception e) {
            return CommonResult.failed(e.getMessage());
        }

    }

    public CommonResult handleKsFlowGraphById(KsFlowTask ksFlowTask, Integer ksFlowGraphId, String createBy, String showName) {
        KsFlowGraph ksFlowGraph = ksFlowGraphService.findById(ksFlowGraphId).get();
        ksFlowGraph.setKsFlowTaskId(ksFlowTask.getId());
        ksFlowGraph.setCreatedBy(createBy);
        ksFlowGraph.setCreatedByShowName(showName);
        ksFlowGraph.setTaskFlag(ksFlowTask.getTaskFlag());
        handleKsFlowGraph(ksFlowGraph);
        return CommonResult.success("", "任务提交成功");
    }

    public void handleKsFlowGraph(KsFlowGraph ksFlowGraph) {
        if (KsFlowGraphHandler.isKsFlowGraphRunning(ksFlowGraph.getId())) {
            throw new RuntimeException("执行失败，该流程图运行中");
        }
        ClueAutoResult param = new ClueAutoResult();
        param.setFilterId(ksFlowGraph.getId());
        param.setFilterName(ksFlowGraph.getName());
        clueAutoResultService.deleteAll(param);
        ksFlowGraphHandler.handleKsFlowGraph(ksFlowGraph);
    }


    public void allFilterToKsFlowGraph() {
        List<Integer> all = queryFactory.select(qKsFilter.id).from(qKsFilter).fetch();
        for (Integer id : all) {
            filterToKsFlowGraph(id);
        }
    }

    public void filterToKsFlowGraph(Integer filterId) {
        String sparkNodeJson = "{\n" +
                "  \"code\": \"start\",\n" +
                "  \"nodeName\": \"开始\",\n" +
                "  \"nextSparkNodes\": [\n" +
                "    {\n" +
                "      \"code\": \"file-677-b2ea5ca0-5d04-11ee-8683-df02a1fd90f7\",\n" +
                "      \"nodeName\": \"数据源\",\n" +
                "      \"nodeExpression\": #sourceConfig,\n" +
                "      \"nextSparkNodes\": [\n" +
                "        {\n" +
                "          \"code\": \"rule-1000264-d9776670-5d03-11ee-8683-df02a1fd90f7\",\n" +
                "          \"nodeName\": \"模型\",\n" +
                "          \"nodeExpression\":#rule,\n" +
                "          \"nextSparkNodes\": [\n" +
                "            {\n" +
                "              \"code\": \"end\",\n" +
                "              \"nodeName\": \"结束\",\n" +
                "              \"points\": {\n" +
                "                \"targets\": [\n" +
                "                  \"end\"\n" +
                "                ],\n" +
                "                \"sources\": []\n" +
                "              }\n" +
                "            }\n" +
                "          ],\n" +
                "          \"points\": {\n" +
                "            \"targets\": [\n" +
                "              \"target-d9776671-5d03-11ee-8683-df02a1fd90f7\"\n" +
                "            ],\n" +
                "            \"sources\": [\n" +
                "              \"source-d9776672-5d03-11ee-8683-df02a1fd90f7\"\n" +
                "            ]\n" +
                "          }\n" +
                "        }\n" +
                "      ],\n" +
                "      \"points\": {\n" +
                "        \"targets\": [\n" +
                "          \"target-b2ea83b0-5d04-11ee-8683-df02a1fd90f7\"\n" +
                "        ],\n" +
                "        \"sources\": [\n" +
                "          \"source-b2ea83b1-5d04-11ee-8683-df02a1fd90f7\"\n" +
                "        ]\n" +
                "      }\n" +
                "    }\n" +
                "  ]\n" +
                "}";
        String graphJson = "{\n" +
                "  \"description\": \"\",\n" +
                "  \"authorityValue\": \"\",\n" +
                "  \"chartData\": {\n" +
                "    \"nodes\": [\n" +
                "      {\n" +
                "        \"id\": \"start\",\n" +
                "        \"points\": {\n" +
                "          \"targets\": [],\n" +
                "          \"sources\": [\n" +
                "            \"start\"\n" +
                "          ]\n" +
                "        },\n" +
                "        \"position\": {\n" +
                "          \"left\": 404,\n" +
                "          \"top\": 50\n" +
                "        },\n" +
                "        \"data\": {\n" +
                "          \"value\": \"开始\",\n" +
                "          \"icon\": \"el-icon-s-home\"\n" +
                "        }\n" +
                "      },\n" +
                "      {\n" +
                "        \"id\": \"end\",\n" +
                "        \"points\": {\n" +
                "          \"targets\": [\n" +
                "            \"end\"\n" +
                "          ],\n" +
                "          \"sources\": []\n" +
                "        },\n" +
                "        \"position\": {\n" +
                "          \"left\": 404,\n" +
                "          \"top\": 551\n" +
                "        },\n" +
                "        \"data\": {\n" +
                "          \"value\": \"结束\",\n" +
                "          \"icon\": \"el-icon-switch-button\"\n" +
                "        }\n" +
                "      },\n" +
                "      {\n" +
                "        \"id\": \"rule-1000264-d9776670-5d03-11ee-8683-df02a1fd90f7\",\n" +
                "        \"points\": {\n" +
                "          \"targets\": [\n" +
                "            \"target-d9776671-5d03-11ee-8683-df02a1fd90f7\"\n" +
                "          ],\n" +
                "          \"sources\": [\n" +
                "            \"source-d9776672-5d03-11ee-8683-df02a1fd90f7\"\n" +
                "          ]\n" +
                "        },\n" +
                "        \"position\": {\n" +
                "          \"left\": 391,\n" +
                "          \"top\": 275\n" +
                "        },\n" +
                "        \"data\": {\n" +
                "          \"icon\": \"el-icon-s-operation\",\n" +
                "          \"value\": \"#ruleName\",\n" +
                "          \"node\": #rule\n" +
                "        }\n" +
                "      },\n" +
                "      {\n" +
                "        \"id\": \"file-677-b2ea5ca0-5d04-11ee-8683-df02a1fd90f7\",\n" +
                "        \"points\": {\n" +
                "          \"targets\": [\n" +
                "            \"target-b2ea83b0-5d04-11ee-8683-df02a1fd90f7\"\n" +
                "          ],\n" +
                "          \"sources\": [\n" +
                "            \"source-b2ea83b1-5d04-11ee-8683-df02a1fd90f7\"\n" +
                "          ]\n" +
                "        },\n" +
                "        \"position\": {\n" +
                "          \"left\": 387,\n" +
                "          \"top\": 157\n" +
                "        },\n" +
                "        \"data\": {\n" +
                "          \"icon\": \"el-icon-s-order\",\n" +
                "          \"value\": \"#sourceConfigName\",\n" +
                "          \"node\": #sourceConfig\n" +
                "        }\n" +
                "      }\n" +
                "    ],\n" +
                "    \"endpoints\": [\n" +
                "      {\n" +
                "        \"id\": \"start\",\n" +
                "        \"data\": {\n" +
                "          \"value\": \"开始\"\n" +
                "        }\n" +
                "      },\n" +
                "      {\n" +
                "        \"id\": \"end\",\n" +
                "        \"data\": {\n" +
                "          \"value\": \"结束\"\n" +
                "        }\n" +
                "      },\n" +
                "      {\n" +
                "        \"id\": \"target-d9776671-5d03-11ee-8683-df02a1fd90f7\",\n" +
                "        \"data\": {\n" +
                "          \"value\": \"输入\"\n" +
                "        }\n" +
                "      },\n" +
                "      {\n" +
                "        \"id\": \"source-d9776672-5d03-11ee-8683-df02a1fd90f7\",\n" +
                "        \"data\": {\n" +
                "          \"value\": \"输出\"\n" +
                "        }\n" +
                "      },\n" +
                "      {\n" +
                "        \"id\": \"target-b2ea83b0-5d04-11ee-8683-df02a1fd90f7\",\n" +
                "        \"data\": {\n" +
                "          \"value\": \"输入\"\n" +
                "        }\n" +
                "      },\n" +
                "      {\n" +
                "        \"id\": \"source-b2ea83b1-5d04-11ee-8683-df02a1fd90f7\",\n" +
                "        \"data\": {\n" +
                "          \"value\": \"输出\"\n" +
                "        }\n" +
                "      }\n" +
                "    ],\n" +
                "    \"edges\": [\n" +
                "      \"start&&target-b2ea83b0-5d04-11ee-8683-df02a1fd90f7\",\n" +
                "      \"source-b2ea83b1-5d04-11ee-8683-df02a1fd90f7&&target-d9776671-5d03-11ee-8683-df02a1fd90f7\",\n" +
                "      \"source-d9776672-5d03-11ee-8683-df02a1fd90f7&&end\"\n" +
                "    ]\n" +
                "  }\n" +
                "}";

        KsFilter ksFilter = queryFactory.selectFrom(qKsFilter).where(qKsFilter.id.eq(filterId)).fetchFirst();
        String filterBundle = ksFilter.getFilterBundle();

        FilterService.FilterBundle bundle = JSON.parseObject(filterBundle, FilterService.FilterBundle.class);
        if (bundle.getFilterParams().isEmpty()) {
            return;
        }
        FilterService.FilterParam filterParam = bundle.getFilterParams().get(0);

        Integer ksRuleId = filterParam.getKsRuleId();
        KsRule ksRule = queryFactory.selectFrom(qKsRule).where(qKsRule.id.eq(ksRuleId)).fetchFirst();
        if (ksRule == null) {
            return;
        }
        ksRule.setPushClueAutoResultFlag(1);

        String ruleName = ksRule.getRuleName();

        String tableName = filterParam.getTableName();
        String dbCode = filterParam.getDbCode();

        BooleanExpression booleanExpression = qSysSourceConfig.targetTable.eq(tableName);
        if (dbCode != null) {
            booleanExpression = booleanExpression.and(qSysSourceConfig.dbCode.eq(dbCode));
        }

        SysSourceConfig sysSourceConfig = queryFactory.selectFrom(qSysSourceConfig).where(booleanExpression).fetchFirst();

        String graphJsonStr = graphJson.replace("#ruleName", ruleName).replace("#rule", JSON.toJSONString(ksRule)).replace("#sourceConfigName", tableName).replace("#sourceConfig", JSON.toJSONString(sysSourceConfig));
        String sparkNodeJsonStr = sparkNodeJson.replace("#ruleName", ruleName).replace("#rule", JSON.toJSONString(ksRule)).replace("#sourceConfigName", tableName).replace("#sourceConfig", JSON.toJSONString(sysSourceConfig));

        KsFlowGraph ksFlowGraph = new KsFlowGraph();
        ksFlowGraph.setGraphJson(graphJsonStr);
        ksFlowGraph.setSparkNodeJson(sparkNodeJsonStr);
        ksFlowGraph.setName(ksFilter.getFilterName());
        ksFlowGraph.setPower(ksFilter.getPower());
        ksFlowGraph.setCreatedBy(ksFilter.getCreatedBy());
        ksFlowGraph.setCreatedByShowName(ksFilter.getCreatedByShowName());
        ksFlowGraph.setUpdatedBy(ksFilter.getUpdatedBy());

        KsFlowGraph ksFlowGraphExist = queryFactory.selectFrom(qKsFlowGraph).where(qKsFlowGraph.name.eq(ksFilter.getFilterName())).fetchFirst();
        if (ksFlowGraphExist == null) {
            ksFlowGraphService.save(ksFlowGraph);
        } else {
            ksFlowGraphExist.setGraphJson(graphJsonStr);
            ksFlowGraphService.save(ksFlowGraphExist);
        }
    }
}
